Skip to content

feat: add lazyPlugins() helper for conditional plugin loading#1215

Open
fengmk2 wants to merge 1 commit intomainfrom
how-to-use-lazy-field
Open

feat: add lazyPlugins() helper for conditional plugin loading#1215
fengmk2 wants to merge 1 commit intomainfrom
how-to-use-lazy-field

Conversation

@fengmk2
Copy link
Copy Markdown
Member

@fengmk2 fengmk2 commented Mar 30, 2026

Summary

Add lazyPlugins() helper that conditionally loads plugins based on the VP_COMMAND environment variable. Plugins load for vite commands (dev, build, test, preview) and when VP_COMMAND is unset (e.g., running vitest directly or via VS Code extension). Skipped for non-vite commands (lint, fmt, check, etc.).

Refs vitejs/vite#22085

Usage

import { defineConfig, lazyPlugins } from 'vite-plus'

// sync
export default defineConfig({
  plugins: lazyPlugins(() => [fooPlugin()]),
})

// async with dynamic import
export default defineConfig({
  plugins: lazyPlugins(async () => {
    const { default: heavyPlugin } = await import('vite-plugin-heavy');
    return [heavyPlugin()];
  }),
})

How it works

  • bin.ts sets VP_COMMAND env var from the CLI command argument
  • Rust resolver injects VP_COMMAND into child process envs for synthesized subcommands (fixes vp run build case)
  • lazyPlugins checks VP_COMMAND — executes callback for vite commands, returns undefined otherwise
  • Async callbacks have their Promise wrapped in an array internally for Vite's asyncFlatten

Test plan

  • Unit tests: 30 tests — lazyPlugins behavior, type compatibility with plugins field, defineConfig compatibility, vitest plugin hooks
  • Snap tests: 6 cases — sync build, async build, vitest plugin, skip-on-lint, vp run build, vp run lint
  • vp check passes (format + lint)
  • Windows CI: forward-slash path normalization fix for snap test stability

🤖 Generated with Claude Code

@netlify
Copy link
Copy Markdown

netlify bot commented Mar 30, 2026

Deploy Preview for viteplus-preview canceled.

Name Link
🔨 Latest commit c1d88f4
🔍 Latest deploy log https://app.netlify.com/projects/viteplus-preview/deploys/69dcd9709309d50008b3d44a

Copy link
Copy Markdown
Member Author

fengmk2 commented Mar 30, 2026


How to use the Graphite Merge Queue

Add the label auto-merge to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@fengmk2 fengmk2 self-assigned this Mar 30, 2026
@fengmk2 fengmk2 marked this pull request as ready for review March 30, 2026 12:32
@fengmk2 fengmk2 force-pushed the how-to-use-lazy-field branch from 5adc3fd to 49d17df Compare March 30, 2026 12:34
Copy link
Copy Markdown
Contributor

@leaysgur leaysgur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no objection to recommending this method as a current workaround. 👌🏻

(However, I cannot judge whether this is the best approach, as I do not know the background behind why this lazy was introduced.)

@cpojer
Copy link
Copy Markdown
Member

cpojer commented Mar 31, 2026

I'm really not a fan of the lazy attribute and I don't think we should recommend it. It would be better to make the plugins field either an array or a function that returns Promise<Array<…>>.

@fengmk2 fengmk2 force-pushed the how-to-use-lazy-field branch from 49d17df to b3cb2f5 Compare March 31, 2026 03:08
@fengmk2 fengmk2 changed the title docs: add lazy loading plugins documentation and tests docs: add config troubleshooting page with lazy loading guide Mar 31, 2026
@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented Mar 31, 2026

I'm really not a fan of the lazy attribute and I don't think we should recommend it. It would be better to make the plugins field either an array or a function that returns Promise<Array<…>>.

This needs to be resolved on the Vite side. I think Vite+ should not change the meaning of fields defined by Vite. @sapphi-red

@fengmk2 fengmk2 force-pushed the how-to-use-lazy-field branch from b3cb2f5 to 8936d0c Compare March 31, 2026 03:31
@fengmk2 fengmk2 changed the title docs: add config troubleshooting page with lazy loading guide docs: add config troubleshooting page with lazy plugin loading guide Mar 31, 2026
@fengmk2 fengmk2 marked this pull request as draft March 31, 2026 05:18
@cpojer
Copy link
Copy Markdown
Member

cpojer commented Mar 31, 2026

Upstream issue created here: vitejs/vite#22085

@fengmk2 fengmk2 closed this Apr 2, 2026
@fengmk2 fengmk2 reopened this Apr 3, 2026
@fengmk2 fengmk2 force-pushed the how-to-use-lazy-field branch from 8936d0c to 71d3659 Compare April 3, 2026 07:25
@fengmk2 fengmk2 changed the title docs: add config troubleshooting page with lazy plugin loading guide feat: add vitePlugins() helper for conditional plugin loading Apr 3, 2026
@fengmk2 fengmk2 marked this pull request as ready for review April 3, 2026 08:17
@fengmk2 fengmk2 added the test: e2e Auto run e2e tests label Apr 3, 2026
@cpojer
Copy link
Copy Markdown
Member

cpojer commented Apr 6, 2026

I don't really follow, why do we need to add a new function to wrap plugins instead of redefining the plugin type from plugins: Array<Plugin> to plugins: Array<Plugin> | () => Promise<Array<Plugin>>?

@TheAlexLichter
Copy link
Copy Markdown
Member

I don't really follow, why do we need to add a new function to wrap plugins instead of redefining the plugin type from plugins: Array<Plugin> to plugins: Array<Plugin> | () => Promise<Array<Plugin>>?

That only works if we'd "guarantee" there are no plugins that can influence the behavior of vp lint, vp fmt etc.

@cpojer
Copy link
Copy Markdown
Member

cpojer commented Apr 6, 2026

For now, that's the expectation, yes.

@TheAlexLichter
Copy link
Copy Markdown
Member

I think then we can also wrap the whole function (as long there is no type mismatch or we alter the allowed types of plugins).

@fengmk2 fengmk2 changed the title feat: add vitePlugins() helper for conditional plugin loading feat: extend defineConfig to support lazy plugin loading via factory functions Apr 7, 2026

This comment was marked as outdated.

chatgpt-codex-connector[bot]

This comment was marked as outdated.

@fengmk2 fengmk2 force-pushed the how-to-use-lazy-field branch 2 times, most recently from 4f0cf8a to 3c27538 Compare April 8, 2026 09:27
@fengmk2 fengmk2 marked this pull request as ready for review April 8, 2026 09:27
@fengmk2 fengmk2 requested a review from TheAlexLichter April 8, 2026 09:27
@fengmk2 fengmk2 force-pushed the how-to-use-lazy-field branch from 3c27538 to 63140ee Compare April 8, 2026 10:06

This comment was marked as duplicate.

chatgpt-codex-connector[bot]

This comment was marked as outdated.

@fengmk2 fengmk2 force-pushed the how-to-use-lazy-field branch from 63140ee to a1c78a7 Compare April 9, 2026 02:50

This comment was marked as outdated.

chatgpt-codex-connector[bot]

This comment was marked as outdated.

Copy link
Copy Markdown
Member

@cpojer cpojer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's beautiful. From a UX perspective, I love it. I don't know if there are any remaining concerns from the Vite side, cc @sapphi-red if you don't mind signing off on this 🙇‍♂️

@fengmk2 fengmk2 force-pushed the how-to-use-lazy-field branch from e9d0689 to db118db Compare April 9, 2026 11:57
@fengmk2 fengmk2 force-pushed the how-to-use-lazy-field branch from db118db to ca6266c Compare April 9, 2026 12:48
@fengmk2

This comment was marked as resolved.

cursor[bot]

This comment was marked as resolved.

@fengmk2 fengmk2 force-pushed the how-to-use-lazy-field branch 4 times, most recently from 4256867 to b11047e Compare April 13, 2026 11:36
@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 13, 2026

@cursor review

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit b11047e. Configure here.

…functions

The `plugins` field in `defineConfig` now accepts factory functions
(`() => PluginOption[]` or `async () => Promise<PluginOption[]>`)
in addition to the standard `PluginOption[]` array.

The factory is only called for vite commands (dev, build, test, preview)
and skipped for non-vite commands (lint, fmt, check, etc.), avoiding
unnecessary plugin loading overhead. When VP_COMMAND is unset (e.g.,
running vitest directly or via VS Code extension), plugins load by default.

VP_COMMAND is set automatically by `vp` in bin.ts and injected into
child process envs by the Rust resolver for synthesized subcommands.

Refs vitejs/vite#22085
@fengmk2 fengmk2 force-pushed the how-to-use-lazy-field branch from b11047e to c1d88f4 Compare April 13, 2026 11:54
@fengmk2 fengmk2 changed the title feat: extend defineConfig to support lazy plugin loading via factory functions feat: add lazyPlugins() helper for conditional plugin loading Apr 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test: e2e Auto run e2e tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants